QuickTime 4 API Documentation

3D Graphics Programming with QuickDraw 3D 1.5.4

Previous | QD3D Book | Overview | Chapter Contents | Next |

Using Meshes

Like the polyhedron and trimesh, the mesh is designed for representing polyhedra. However, it is intended for the interactive topological creation and editing of polyhedra, so its architecture and API were designed to support both iterative construction and topological modification.

Iterative construction means that you can easily construct a mesh by building it face-by-face, instead of filling in a data structure and constructing it from the data structure all at once.

Topological modification means that you can easily add and delete vertices, faces, edges, and other components in a mesh. A mesh has no explicit public data structure; unlike the other geometric primitives, it also has no immediate-mode capability.

Meshes are not intended for representing large-scale polyhedral models with many vertices and faces. If employed this way, the mesh format causes poor I/O behavior, heavy memory usage, and suboptimal rendering speed. Hence modeling, animation, and design applications should use the polyhedron format for most model creation and storage.

On the other hand, in some applications the mesh format is superior to other geometric primitives. For example, it would be ideal in an application that used a 3D sampling peripheral, such as a Polhemus device, to digitize physical objects. You could use the mesh to construct the digitized model face-by-face, to merge or split faces, to add or delete vertices, and so forth. Doing these tasks with an array-based data structure would be awkward to program and force the program to make repeated array reallocations.

The faces of meshes, unlike those of the polyhedron and trimesh, may have more than three vertices, may be concave (though not self-intersecting), and may contain holes by defining faces with more than one list of vertices.

The mesh API supports a rich variety of geometric and topological editing operations, but only for retained mode; it has no immediate-mode public data structure. If your application needs immediate mode, you should use the polyhedron format.

In general, the rendering speed of meshes is relatively slow. They must be either traversed for rendering or decomposed into other primitives that yield faster rendering. Traversing usually results in the slow retransformation and reshading of shared vertices, while decomposition may require heavy memory usage as well as complex and slow bookkeeping code.

To summarize, you should use the mesh primitive for interactive construction and topological editing. Its rich set of geometric and topological editing calls, the ability to make nontriangular faces directly, the ability to make concave faces and faces with holes, and the consistent use of attribute sets make the mesh primitive ideal for many purposes. In addition, the 3D metafile representation of a mesh is quite space efficient. Because the mesh lacks an immediate mode, however, it requires a large amount of memory and may be inefficient for other uses.

Creating a Mesh

As explained in "Meshes" , you create a mesh by calling Q3Mesh_New to create a new empty mesh and then by calling Q3Mesh_VertexNew and Q3Mesh_FaceNew to explicitly add vertices and faces to the mesh. Listing 9 illustrates how to create a simple mesh using these functions. It also shows how to attach a custom surface parameterization to a mesh face, so that a texture can be mapped onto the face.

Listing 9 Creating a simple mesh

TQ3GroupObject MyBuildMesh (void)
{
    TQ3ColorRGB                         myMeshColor;
    TQ3GroupObject                      myModel;
    static TQ3Vertex3D                  vertices[9] = {
        { { -0.5,  0.5, 0.0 }, NULL },
        { { -0.5, -0.5, 0.0 }, NULL },
        { { 0.0, -0.5, 0.3 }, NULL },
        { { 0.5, -0.5, 0.0 }, NULL },
        { { 0.5,  0.5, 0.0 }, NULL },
        { { 0.0,  0.5, 0.3 }, NULL },
        { { -0.4,  0.2, 0.0 }, NULL },
        { { 0.0,  0.0, 0.0 }, NULL },
        { { -0.4, -0.2, 0.0 }, NULL }};
    static TQ3Param2D                   verticesUV[9] = {
        {0.0, 1.0}, {0.0, 0.0}, {0.5, 0.0}, {1.0, 0.0},
        {1.0, 1.0}, {0.5, 1.0}, {0.1, 0.8}, {0.5, 0.5},
        {0.1, 0.4}};
    TQ3MeshVertex                       myMeshVertices[9];
    TQ3GeometryObject                   myMesh;
    TQ3MeshFace                         myMeshFace;
    TQ3AttributeSet                     myFaceAttrs;
    unsigned long                       i;
    myMesh = Q3Mesh_New();              /*create new empty mesh*/
    Q3Mesh_DelayUpdates(myMesh);        /*turn off mesh updating*/
    /*Add vertices and surface parameterization to mesh.*/
    for (i = 0; i < 9; i++) {
        TQ3AttributeSet                 myVertAttrs;
        myMeshVertices[i] = Q3Mesh_VertexNew(myMesh, &vertices[i]);
        myVertAttrs = Q3AttributeSet_New();
        Q3AttributeSet_Add(myVertAttrs, kQ3AttributeTypeSurfaceUV, &verticesUV[i]);
        Q3Mesh_SetVertexAttributeSet(myMesh, myMeshVertices[i], myVertAttrs);
        Q3Object_Dispose(myVertAttrs);
    }
    myFaceAttrs = Q3AttributeSet_New();
    myMeshColor.r = 0.3;
    myMeshColor.g = 0.9;
    myMeshColor.b = 0.5;
    Q3AttributeSet_Add(myFaceAttrs, kQ3AttributeTypeDiffuseColor, &myMeshColor);
    myMeshFace = Q3Mesh_FaceNew(myMesh, 6, myMeshVertices, myFaceAttrs);
    Q3Mesh_FaceToContour(myMesh, myMeshFace,
                        Q3Mesh_FaceNew(myMesh, 3, &myMeshVertices[6], NULL));
    Q3Mesh_ResumeUpdates(myMesh);
    myModel = Q3OrderedDisplayGroup_New();
    Q3Group_AddObject(myModel, myMesh);
    Q3Object_Dispose(myFaceAttrs);
    Q3Object_Dispose(myMesh);
    return (myModel);
}

The new mesh created by MyBuildMesh is a retained object. Note that you need to call Q3Mesh_New before you call Q3Mesh_VertexNew and Q3Mesh_FaceNew . Also, the call to Q3Mesh_FaceToContour destroys any attributes associated with the mesh face that is turned into a contour.

Traversing a Mesh

QuickDraw 3D supplies functions that you can use to traverse a mesh by iterating through various parts of the it. For example, you can operate on each face of a mesh by calling the Q3Mesh_FirstMeshFace function to get the first face in the mesh and then Q3Mesh_NextMeshFace to get each successive face. When you call Q3Mesh_FirstMeshFace , you specify a mesh and a mesh iterator structure, which QuickDraw 3D fills in with information about its current position while traversing the mesh. You must pass that same mesh iterator structure to Q3Mesh_NextMeshFace when you get successive faces in the mesh. Listing 10 illustrates how to use these routines to operate on all faces in a mesh.

Listing 10 Iterating through all faces in a mesh

TQ3Status MySetMeshFacesDiffuseColor (TQ3GeometryObject myMesh,
                                      TQ3ColorRGB color)
{
    TQ3MeshFace                 myFace;
    TQ3MeshIterator             myIter;
    TQ3Status                   myErr;
    TQ3AttributeSet             mySet;
    for (myFace = Q3Mesh_FirstMeshFace(myMesh, &myIter);
          myFace;
          myFace = Q3Mesh_NextMeshFace(&myIter)) {
        /*Get the current attribute set of the current face.*/
        myErr = Q3Mesh_GetFaceAttributeSet(myMesh, myFace, &mySet);
        if (myErr == kQ3Failure) return (kQ3Failure);
        /*Add the color attribute to the face attribute set.*/
        myErr = Q3AttributeSet_Add((TQ3AttributeSet)mySet,
                                    kQ3AttributeTypeDiffuseColor, &color);
        if (myErr == kQ3Failure) return (kQ3Failure);
        /*Set the attribute set of the current face.*/
        myErr = Q3Mesh_SetFaceAttributeSet(myMesh, myFace, mySet);
        if (myErr == kQ3Failure) return (kQ3Failure);
    }
    return (kQ3Success);
}

QuickDraw 3D also supplies a number of C language macros that you can use to simplify your source code when traversing a mesh. For example, you can use the following Q3ForEachMeshFace macro:

#define Q3ForEachMeshFace(m,f,i)            
    for ( (f) = Q3Mesh_FirstMeshFace((m),(i));
        (f);                                
        (f) = Q3Mesh_NextMeshFace((i)) )

Listing 11 shows how to use two of these macros to attach a corner to each vertex or each face of a mesh.

Listing 11 Attaching corners to all vertices in all faces of a mesh

TQ3Status MyAddCornersToMesh (TQ3GeometryObject myMesh,
                              TQ3AttributeSet mySet)
{
    TQ3MeshFace                 myFace;
    TQ3MeshVertex               myVertex;
    TQ3MeshIterator             myIter1;
    TQ3MeshIterator             myIter2;
    TQ3Status                   myErr;
    Q3ForEachMeshFace(myMesh, myFace, &myIter1) {
        Q3ForEachFaceVertex(myFace, myVertex, &myIter2) {
            myErr = Q3Mesh_SetCornerAttributeSet
                        (myMesh, myFace, myVertex, mySet);
            if (myErr == kQ3Failure) return (kQ3Failure);
        }
    }
    return (kQ3Success);
}

© 1997 Apple Computer, Inc.

Previous | QD3D Book | Overview | Chapter Contents | Next |